#pragma once
/*///////////////////////////////////////////////////////////////////////
								CEventHandler
									|
								CBaseSocket
			|-----------------------|------------------------|
		CSockListner			CSockStream				CSockDatagram
									
///////////////////////////////////////////////////////////////////////*/

#include "IOContext.h"
#include "SockSystem.h"
#include "KeyGenerator.h"
#include "../security/blowfish.h"
#include "../security/keyexchange.h"

//////////////////////////////////////////////////////////////////
// CBaseSocket
//////////////////////////////////////////////////////////////////
class CBaseSocket : public CEventHandler
{
public:
	enum eSockType
	{
		eST_Stream,
		eST_DGram,
		eST_Listener,
		eST_Count,
		eST_Invalid = -1
	};

protected:
	CBaseSocket();
	virtual ~CBaseSocket();

protected:
	SOCKET				m_hSocket;
	SESSION_TYPE		m_SessionType;
	
	BOOL				m_bGracefulShutdown;
	CCriticalSectionBS	m_CS;
	eSockType			m_Type;
	
protected:
	virtual void	Reset() {}
	BOOL			Create(SESSION_TYPE session_type);

public:
	virtual long	Close();
	BOOL	IsValidSock() 
	{ 
		if (this == NULL)
			return FALSE;

		return (m_hSocket != INVALID_SOCKET); 
	}

	SOCKET			GetHandle() { return m_hSocket; }

	eSockType		GetType(){ return m_Type;}

	sockaddr_in		GetLocalIp();
	unsigned short	GetLocalPort();
};

//////////////////////////////////////////////////////////////////
// CSockStream
//////////////////////////////////////////////////////////////////
class CMsg;
class IFile;
class CSession;

class CSockStream : public CBaseSocket
{
public:
	CSockStream();
	virtual ~CSockStream();

protected:
	BSSOCKADDR_IN	m_addrPeer;
	
	CIOContext		m_ctxWrite;
	CIOContext		m_ctxRead;
	CIOContext		m_ctxDisconnect;
			
	DWORD			m_dwLatestAccessTick;	// keep latest recv occur to check stale session

	DWORD			m_dwdisconnectreason;
	
	CMsg*			m_pPendingSend;
	CMsg*			m_pPendingRecv;
	CSession*		m_pSession;	
    
	IQue<CMsg*>*	m_pSendingQue;

	long			m_dwCurSentBytes;
	DWORD			m_dwCurRecvBytes;
	long			m_nOutstandingSendCount;

	CTask*			m_pTask;

	////////////////////////////////////////// msg protector
	SESSION_STATE	m_SessionState;

	CBlowFish			m_BF;
	CRC32				m_Crc;	
	CSeqNumGenerator	m_SeqGenerator;
	
	BYTE			m_cMsgEncodingOption;
	
	// new security
	CKeySender		m_Sender;
	CKeyRepicient	m_Recipient;

	// seucurity
	BYTE			m_HandShake;

	////////////////////////////////////////// completion handlers
	long			(CSockStream::*FP_EVENTHANDLER[IOTYPE_NUM][IO_TARGET_NUM])(IIOContext* pContext, DWORD dwTransferred);

	long			(CSockStream::*FP_RECVED_MSG_HANDLER)(CMsg* pRecvedMsg);

	long			(CSockStream::*FP_INVOKER_SENDDATA)(CMsg* pMsg);

private:
	BOOL			SecurityModeCheck( void );	
	
protected:
	virtual BOOL	RegisterHandleTo(CServiceObject* pServiceObj, HANDLE hHandle);	
	
	void			Invalidate();

	BOOL			CreateWSAEvent();

	BOOL			GetMsgToSend();
	BOOL			CheckEncodingOption(BYTE flag) { return (m_cMsgEncodingOption & flag); }

	void			PrepareSocket();
	
	// msg coder / decoder
	void			_SetEncodeCtx(EncodeCtx& ec);

	BOOL			ProtectMsg(CMsg*& pMsg);
	long			NeutralizeMsg(WORD& wDataSize);

	// msg forger
	long			_ForgeMsgToSend(CMsg*& pMsg);
	
	// send
	long			_SendMsgDirect(CMsg* pMsg);
	long			_SendMsgBuffered(CMsg* pMsg);
	
	long			_OnDataSentDirect(IIOContext* pContext, DWORD dwTransferred);
	long			_OnDataSentBuffered(IIOContext* pContext, DWORD dwTransferred);
	long			_OnFileSent(IIOContext* pContext, DWORD dwTransferred);

	// recv
	long			_OnDataRecved(IIOContext* pContext, DWORD dwTransferred);

	long			_WriteFileData(CMsg* pFileMsg);

	long			_OnMsgReceivedBeforeHandshake(CMsg* pRecvMsg);
	long			_OnMsgReceivedAfterHandshake(CMsg* pRecvMsg);
	void			_SessionHandshaked( void );
	long			_MsgReceived(CMsg* pRecvedMsg);

	// posting
	long			PostSend(IIOContext* pIOContext);
	long			PostRecv();

public:
	void			Init();
	virtual long	Close();
	void			TimeStamp() { m_dwLatestAccessTick = ::GetTickCount(); }
	DWORD			GetLatestAccessTick() const { return m_dwLatestAccessTick; }
	BOOL			LetsRock(CServiceObject* pServiceObject) { return RegisterHandleTo(pServiceObject, (HANDLE)GetHandle()); }
	
	sockaddr_in		GetPeerIp()						{ return *(struct sockaddr_in*)&m_addrPeer; }
	void			SetSession(CSession* pSession)	{ m_pSession = pSession; }
	CSession*		GetSession()					{ return m_pSession; }
	DWORD			GetSessionID();
	void			SetCurTask(CTask* pTask)		{ _ASSERT(pTask != NULL); SCOPED_LOCK_SINGLE(&m_CS); m_pTask = pTask; }
	long			GetCountOfOutstandingSend()		{ return m_nOutstandingSendCount; }
	CIOContext*		GetContext(long nOP);
	DWORD			GetElapsedTick();
	SESSION_TYPE	GetSessionType() { return m_SessionType; }
	BOOL			IsLiveSession(int& nEstablishedSeconds);

	BOOL			CreateActiveSession(BSSOCKADDR_IN* addr_connect, BSSOCKADDR_IN* addr_bind = NULL,BOOL bForKeepAlive = FALSE);
	BOOL			CreatePassiveSession(SOCKET hListener);
	long			OnAccepted(SOCKET hListener);
	
	long			SendMsg(CMsg* pMsg);
	long			SendFile(IFile* pFile, DWORD dwOffset);
	long			RecvFile();

	BOOL			ProtectSession(EncodeCtx& ec);
	
	virtual long	HandleEvent(DWORD dwTransferred, void* pContext);

	BYTE			Disconnect(DWORD dwDisconnectReason);
	DWORD			GetDisconnectReason() { return m_dwdisconnectreason; }
	void			SetDisconnectReason( DWORD dwDisconnectReason );
	
	friend class CSession;
};

inline CIOContext* CSockStream::GetContext(long nOP)
{
	switch (nOP)
	{
	case IO_READ:
		return &m_ctxRead;
	case IO_WRITE:
		return &m_ctxWrite;
	default:
		_ASSERT(0);
	}
	return NULL;
}

//////////////////////////////////////////////////////////////////
// CSockListener
//////////////////////////////////////////////////////////////////
class CSockListener : public CBaseSocket
{
public:
	CSockListener();
	virtual ~CSockListener();

protected:
	long			m_nBackLog;

	std::list<CSockStream*> m_socketlist;

	CCriticalSectionBS	m_AcceptCS;

public:
	static	long	s_nCurPostedAccept;
		
public:
	virtual long	Close();
	virtual long	HandleEvent(DWORD dwTransferred, void* pContext);
	virtual BOOL	RegisterHandleTo(CServiceObject* pServiceObj, HANDLE hHandle);

	BOOL			Create(BSSOCKADDR_IN* addr_bind, int backlog = DEFAULT_BACK_LOG);
	BOOL			Listen(CServiceObject* pServiceObj);
	long			Accept(CSockStream* pSock);
	
	BOOL			PostReuseAccept(CSockStream* pSock);
	BOOL			DisconnectCheck( CSockStream* pSock, DWORD dwReason );
	
protected:
	void			PostNewAccept();
};










































